package com.kehutong.payfees.service;

import com.kehutong.common.DoveClient;
import com.kehutong.common.enums.ResidentType;
import com.kehutong.common.math.CalculatorUtils;
import com.kehutong.payfees.entity.ChargeBill;
import com.kehutong.payfees.entity.ChargeCallTarget;
import com.kehutong.payfees.entity.ChargeCallTask;
import com.kehutong.payfees.enums.*;
import com.kehutong.payfees.util.BasicClient;
import org.coraframework.inject.Inject;
import org.coraframework.json.JSONObject;
import org.coraframework.logger.Logger;
import org.coraframework.logger.LoggerFactory;
import org.coraframework.mvc.webservice.WebService;
import org.coraframework.orm.Pool;
import org.coraframework.orm.jdbc.JdbcSession;
import org.coraframework.orm.jdbc.connection.ThreadLocalCache;
import org.coraframework.threadpool.ThreadPool;
import org.coraframework.util.Objects;

import java.time.LocalDate;
import java.util.List;

/**
 * Author:  peter
 * Date:    2020/7/10
 * Function:缴费模块-催缴
 */
@WebService("/payfees/charge/call")
public class ChargeCallServiceImpl {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Inject
    private BasicClient basicClient;
    @Inject
    protected JdbcSession jdbcSession;
    @Inject
    protected ThreadPool threadPool;
    @Inject
    private ChargeLogServiceImpl chargeLogService;
    @Inject
    private DoveClient doveClient;

    /**
     * 更新催缴任务后
     * 处理立即催缴
     */
    public void afterCallTaskUpdate(ChargeCallTask task,String corpId) throws Exception{
        if (task.getCallMethod() == BillCallMethod.IMMEDIATELY) {
            asyncBuildRunCallTask(task,corpId);
        }
    }

    private JSONObject buildMpMessageData(String value) {
        JSONObject json = new JSONObject();
        json.put("value", value);
        return json;
    }

    public String getFullHouseNameFromBill(ChargeBill bill) {
        if (bill == null) {
            return null;
        }

        return String.format("%s%s%s%s",
        		bill.getCommunityName(),
                bill.getBuildingName(),
                Objects.isEmpty(bill.getUnitName()) ? "" : bill.getUnitName(),
                bill.getHouseName());
    }


    public ChargeCallTask findOneUnRunCallTaskByBill(ChargeBill bill) {

        List<ChargeCallTask> callTasks = jdbcSession.findArray(ChargeCallTask.class)
                .eq("run", false)
                .eq("billNo", bill.getId())
                .exe();

        if (!callTasks.isEmpty()) {
            return callTasks.get(0);
        }
        // 新建
        ChargeCallTask task = Pool.newInstance(ChargeCallTask.class);
        task.setBillNo(bill.getId());
        jdbcSession.insert(task);

        return task;
    }

    /**
     * 执行时，创建发送对象记录
     */
    public void buildCallTargetByTask(ChargeCallTask task,String corpId) {

        ChargeBill bill = Pool.get(ChargeBill.class, task.getBillNo());

        try {
            JSONObject result = doveClient.post("/crm/resident/house/service/getHouseOwner", http->{
                http.addHeader("companyNo", corpId);
                http.addParam("houseNo", bill.getHouseNo());
            });
            int code = result.getIntValue("code");
            if (code == 200) {
                JSONObject data = result.getJSONObject("data");
                if(Objects.nonEmpty(data)){
                    ChargeCallTarget target = Pool.newInstance(ChargeCallTarget.class);
                    target.setTaskNo(task.getId());
                    target.setAvatar(data.getString("avatar"));
                    target.setNickName(data.getString("realname"));
                    target.setMobile(data.getString("mobile"));
                    target.setResidentType(ResidentType.OWNER);
                    target.setResidentNo(data.getString("residentNo"));
                    jdbcSession.insert(target);
                    task.setToCallCnt(1);
                    jdbcSession.updateById(task);
                }
            }
        }catch (Exception e){
            logger.error("获取房屋业主信息失败:{}",e.getMessage());
        }
    }

    /**
     * 执行缴费任务
     */
    public void runCallTask(ChargeCallTask task,String corpId) {
        if (task.isRun()) {
            return;
        }

        ChargeBill bill = Pool.get(ChargeBill.class, task.getBillNo());

        List<ChargeCallTarget> targets = jdbcSession.findArray(ChargeCallTarget.class)
                .eq("taskNo", task.getId())
                .exe();

        for (ChargeCallTarget target : targets) {
            // 发送公众号推送至目标对象
            sendNotifyPayMpMessageToCallTarget(bill, target,corpId);
        }

        int calledCount = (int) targets.stream().filter(t -> t.getMpCallState() == CallRunState.SEND_SUCCESS).count();

        task.setCalledCnt(calledCount);
        task.setRun(true);

        jdbcSession.updateById(task);
    }

    /**
     * 执行催缴任务发送指定对象
     * 异常处理及记录
     */
    private void sendNotifyPayMpMessageToCallTarget(ChargeBill bill, ChargeCallTarget target,String corpId) {
        try {
            doSendNotifyPayMpMessageToCallTarget(bill, target,corpId);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("sendNotifyPayMpMessageToCallTarget", e);

            target.setMpCallState(CallRunState.SEND_FAILED);
            target.setMpCallRemark("系统错误");
            jdbcSession.updateById(target);

            chargeLogService.saveSystemLog(bill.getId(), BillOperateType.DO_SEND_MP_BILL_NOTIFY, "系统错误");
        }
    }

    /**
     * 微信公众号推送-通知缴费消息
     * 推送给指定住户
     */
    public void doSendNotifyPayMpMessageToCallTarget(ChargeBill bill, ChargeCallTarget target,String corpId) throws Exception {

        JSONObject rs = doveClient.post("/kht-bin/wechat/getMemberMap", http -> {
            http.addParam("khtId",target.getResidentNo());
            http.addParam("companyNo",corpId);
        });
        int code = rs.getIntValue("code");
        if (code != 200) {
            target.setMpCallState(CallRunState.SEND_FAILED);
            target.setMpCallRemark(rs.getString("message"));
            jdbcSession.updateById(target);
            chargeLogService.saveSystemLog(bill.getId(), BillOperateType.DO_SEND_MP_BILL_NOTIFY, "失败："+rs.getString("message"));
            return;
        }
        JSONObject memberMap = rs.getJSONObject("data");
        if(Objects.isEmpty(memberMap)){
            target.setMpCallState(CallRunState.SEND_FAILED);
            target.setMpCallRemark("手机号"+target.getMobile()+"的用户没有关注过公众号");
            jdbcSession.updateById(target);
            chargeLogService.saveSystemLog(bill.getId(), BillOperateType.DO_SEND_MP_BILL_NOTIFY, "失败：手机号"+target.getMobile()+"的用户没有关注过公众号");
            return;
        }

        String openId =memberMap.getString("mp_open_id");
        if(Objects.isBlank(openId)){
            target.setMpCallState(CallRunState.SEND_FAILED);
            target.setMpCallRemark("手机号"+target.getMobile()+"的用户openId不存在");
            jdbcSession.updateById(target);
            chargeLogService.saveSystemLog(bill.getId(), BillOperateType.DO_SEND_MP_BILL_NOTIFY, "失败：手机号"+target.getMobile()+"的用户openId不存在");
            return;
        }

        JSONObject data = new JSONObject();
        data.put("first", buildMpMessageData("本期账单已出，请及时处理，请点击去缴费~"));
        //房产地址
        data.put("keyword1", buildMpMessageData(getFullHouseNameFromBill(bill)));
        //房产用户
        data.put("keyword2", buildMpMessageData(getOwnerName(corpId, bill.getHouseNo())));
        //账单年月
        data.put("keyword3", buildMpMessageData(getShouldPayDate(bill.getShouldPayDate())));
        //账单金额
        data.put("keyword4", buildMpMessageData(CalculatorUtils.formatMoneyString(bill.getTotal()) + "元"));

        data.put("remark", buildMpMessageData("感谢您的使用，祝您生活愉快！"));

        String msg;
        try {
            JSONObject resultSend = doveClient.post("/message/mini/uniformSend", (http) -> {
                http.addParam("openid", openId);
                http.addParam("templateType", "PAY_CALL");
                http.addParam("param", getParam("billNo", bill.getId(), "companyNo", corpId, "houseNo" , bill.getHouseNo()));
                JSONObject body = new JSONObject();
                body.put("data", data);
                http.setBody(body.toString());
            });

            int codeSend = resultSend.getIntValue("code");
            if (codeSend == 200) {
                target.setMpCallState(CallRunState.SEND_SUCCESS);
            }else{
                msg = resultSend.getString("message");
                target.setMpCallState(CallRunState.SEND_FAILED);
                target.setMpCallRemark(msg);
            }
		} catch (Exception e) {
			msg = e.getMessage();
			target.setMpCallState(CallRunState.SEND_FAILED);
			target.setMpCallRemark(msg);
		}
        jdbcSession.updateById(target);
        chargeLogService.saveSystemLog(bill.getId(), BillOperateType.DO_SEND_MP_BILL_NOTIFY, "成功");
    }

    private String getParam(String...params) {
        if (params.length % 2 != 0) {
            throw new IllegalArgumentException("参数必须是偶数!");
        }
        StringBuilder buf = new StringBuilder();

        for (int i = 0; i < params.length; i += 2) {
            buf.append(params[i]).append("=").append(params[i + 1]).append('&');
        }

        int lastIndex = buf.length() - 1;
        if (buf.charAt(lastIndex) == '&') {
            buf.deleteCharAt(lastIndex);
        }

        return buf.toString();
    }

    public String getOwnerName(String companyNo, String houseNo) throws Exception{
        JSONObject result = doveClient.post("/basic/house/service/get", http->{
            http.addHeader("companyNo", companyNo);
            http.addParam("id", houseNo);
        });

        if (Objects.isNull(result) || Objects.isNull(result.getJSONObject("data"))) {
            return "";
        }

        JSONObject data = result.getJSONObject("data");

        return data.getString("ownerName");
    }

    public String getShouldPayDate(LocalDate date) throws Exception {
        StringBuilder build = new StringBuilder();
        build.append(date.getYear())
                .append("年")
                .append(date.getMonthValue())
                .append("月");
        return build.toString();
    }

    /**
     * 异步执行
     * 查找并构建发送对象
     * 根据发送对象执行任务
     */
    public void asyncBuildRunCallTask(ChargeCallTask task,String corpId){
        threadPool.execute(() -> {
            buildRunCallTask(task, corpId);
        });
    }

    public void buildRunCallTask(ChargeCallTask task,String corpId){
        if (!validateTaskRunnable(task, corpId)) {
            return;
        }
        buildCallTargetByTask(task,corpId);
        runCallTask(task,corpId);
        refreshBillCallState(task.getBillNo());
    }

    /**
     * 校验催缴任务是否可执行
     */
    private boolean validateTaskRunnable(ChargeCallTask task, String corpId) {
        ThreadLocalCache.current().setCorpId(corpId);

        ChargeBill bill = Pool.get(ChargeBill.class, task.getBillNo());
        if (bill == null) {
            return false;
        }
        if (bill.isDeleted()) {
            // 已删除
            return false;
        }
        if (bill.getEnableState() == BillEnableState.UNUSED) {
            // 未启用
            return false;
        }
        if (bill.getPaidState() == BillPaidState.PAID) {
            // 已支付
            return false;
        }

        return true;
    }

    /**
     * 根绝催缴任务
     * 刷新缴费账单的催缴状态
     */
    public void refreshBillCallState(String billNo) {
        ChargeBill bill = Pool.get(ChargeBill.class, billNo);
        if (bill == null) {
            return;
        }
        // 取倒序最后一个
        List<ChargeCallTask> tasks = jdbcSession.findArray(ChargeCallTask.class)
                .eq("billNo", bill.getId())
                .order("configCallAt", "DESC")
                .exe();
        if (tasks.isEmpty()) {
            bill.setCallState(null);
            bill.setCallMethod(null);
            bill.setConfigCallAt(null);
        } else {
            ChargeCallTask task = tasks.get(0);
            bill.setCallMethod(task.getCallMethod());
            bill.setConfigCallAt(task.getConfigCallAt());

            if (!task.isRun()) {
                // 未执行
                bill.setCallState(BillCallState.UNDO);
            } else {
                if (task.getCalledCnt() == task.getToCallCnt()
                        && task.getCalledCnt() > 0) {
                    bill.setCallState(BillCallState.DONE);
                } else {
                    bill.setCallState(BillCallState.FAILED);
                }
            }
        }
        jdbcSession.updateById(bill);
    }

}
